﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;

namespace RayD.Core
{
    public class Logger
    {

        static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim();
        static int WritedCount = 0;
        static int FailedCount = 0;
        static string _contentRoot = string.Empty;

        public Logger(string contentPath)
        {
            _contentRoot = contentPath;
        }

        /// <summary>
        /// 记录日志
        /// </summary>
        /// <param name="logCategory">日志类别</param>
        /// <param name="contentParas">日志内容</param>
        /// <param name="dataParas">备份数据</param>
        public static void Log(LogCategory logCategory, string[] contentParas, params string[] dataParas)
        {
            try
            {
                //设置读写锁为写入模式独占资源，其他写入请求需要等待本次写入结束之后才能继续写入
                //注意：长时间持有读线程锁或写线程锁会使其他线程发生饥饿 (starve)。 为了得到最好的性能，需要考虑重新构造应用程序以将写访问的持续时间减少到最小。
                //      从性能方面考虑，请求进入写入模式应该紧跟文件操作之前，在此处进入写入模式仅是为了降低代码复杂度
                //      因进入与退出写入模式应在同一个try finally语句块内，所以在请求进入写入模式之前不能触发异常，否则释放次数大于请求次数将会触发异常
                LogWriteLock.EnterWriteLock();

                var now = DateTime.Now.ToCstTime();
                var folderPath = Path.Combine(_contentRoot, $"{AppSettings.ProjectName.EName}_Logs{Path.DirectorySeparatorChar}{now:yyyy-MM-dd}");
                if (!Directory.Exists(folderPath))
                {
                    Directory.CreateDirectory(folderPath);
                }
                string logFilePath = Path.Combine(folderPath, $"{logCategory}.txt");

                StringBuilder logContent = new StringBuilder(200);
                logContent.Append($"【操作时间】：{now:yyyy-MM-dd HH:mm:ss}\r\n");
                logContent.Append($"【操作人】：{ AuthenticationHelper.LoginUserInfo?.realName }\r\n");
                if (!contentParas.IsEmpty()) logContent.Append(FormatContent(logCategory, contentParas));
                if (dataParas.Length > 0) logContent.Append($"【备份数据】：{String.Join("\r\n", dataParas)}\r\n");
                logContent.Append("------------------------------------------------------------------------------------------------------------------------------------------\r\n");

                File.AppendAllText(logFilePath, logContent.ToString());
                WritedCount++;
            }
            catch (Exception e)
            {
                Console.Write(e.Message);
                FailedCount++;
            }
            finally
            {
                //退出写入模式，释放资源占用
                //注意：一次请求对应一次释放
                //      若释放次数大于请求次数将会触发异常[写入锁定未经保持即被释放]
                //      若请求处理完成后未释放将会触发异常[此模式不下允许以递归方式获取写入锁定]
                LogWriteLock.ExitWriteLock();
            }
        }
        /// <summary>
        /// 格式化日志内容
        /// </summary>
        /// <param name="logCategory">日志类别</param>
        /// <param name="contentParas">日志内容</param>
        /// <returns></returns>
        static string FormatContent(LogCategory logCategory, string[] contentParas)
        {
            //形成信息字符串
            StringBuilder strInfo = new StringBuilder(200);

            switch (logCategory)
            {
                case LogCategory.Operate:
                    strInfo.Append($"【请求地址】：{ contentParas.ElementAtOrDefault(0) ?? "" }\r\n");
                    strInfo.Append($"【请求参数】：{ contentParas.ElementAtOrDefault(1) ?? "" }\r\n");
                    strInfo.Append($"【结果信息】：{ contentParas.ElementAtOrDefault(2) ?? "" }\r\n");
                    break;
                case LogCategory.Exception:
                    strInfo.Append($"【请求地址】：{ contentParas.ElementAtOrDefault(0) ?? "" }\r\n");
                    strInfo.Append($"【请求参数】：{ contentParas.ElementAtOrDefault(1) ?? "" }\r\n");
                    strInfo.Append($"【异常信息】：{ contentParas.ElementAtOrDefault(2) ?? "" }\r\n");
                    break;
                default:
                    strInfo.Append($"【日志内容】：{ String.Join("\r\n", contentParas) }\r\n");
                    break;
            }

            return strInfo.ToString();
        }

        /// <summary>
        /// 读取文件内容
        /// </summary>
        /// <param name="folderPath">文件夹路径</param>
        /// <param name="fileName">文件名</param>
        /// <param name="encode">编码</param>
        /// <param name="readType">读取类型(0:精准,1:前缀模糊)</param>
        /// <returns></returns>
        public static string ReadLog(string folderPath, string fileName, Encoding encode, ReadType readType = ReadType.Accurate)
        {
            string s = "";
            try
            {
                LogWriteLock.EnterReadLock();

                // 根据文件名读取当前文件内容
                if (readType == ReadType.Accurate)
                {
                    var filePath = Path.Combine(folderPath, fileName);
                    if (!File.Exists(filePath))
                    {
                        s = null;
                    }
                    else
                    {
                        StreamReader f2 = new StreamReader(filePath, encode);
                        s = f2.ReadToEnd();
                        f2.Close();
                        f2.Dispose();
                    }
                }

                // 根据前缀读取所有文件内容
                if (readType == ReadType.Prefix)
                {
                    var allFiles = new DirectoryInfo(folderPath);
                    var selectFiles = allFiles.GetFiles().Where(fi => fi.Name.ToLower().Contains(fileName.ToLower())).ToList();

                    foreach (var item in selectFiles)
                    {
                        if (File.Exists(item.FullName))
                        {
                            StreamReader f2 = new StreamReader(item.FullName, encode);
                            s += f2.ReadToEnd();
                            f2.Close();
                            f2.Dispose();
                        }
                    }
                }

                // 根据前缀读取 最新文件 时间倒叙
                if (readType == ReadType.PrefixLatest)
                {
                    var allFiles = new DirectoryInfo(folderPath);
                    var selectLastestFile = allFiles.GetFiles().Where(fi => fi.Name.ToLower().Contains(fileName.ToLower())).OrderByDescending(d => d.Name).FirstOrDefault();

                    if (selectLastestFile != null && File.Exists(selectLastestFile.FullName))
                    {
                        StreamReader f2 = new StreamReader(selectLastestFile.FullName, encode);
                        s = f2.ReadToEnd();
                        f2.Close();
                        f2.Dispose();
                    }
                }
            }
            catch (Exception)
            {
                FailedCount++;
            }
            finally
            {
                LogWriteLock.ExitReadLock();
            }
            return s;
        }

    }

    public enum ReadType
    {
        /// <summary>
        /// 精确查找一个
        /// </summary>
        Accurate,
        /// <summary>
        /// 指定前缀，模糊查找全部
        /// </summary>
        Prefix,
        /// <summary>
        /// 指定前缀，最新一个文件
        /// </summary>
        PrefixLatest
    }
}
